/*
 * 根据分辨率计算核心高度
 * 1）width > 1400 的宽度：默认 - 48
 * 3）width > 1900 的宽度：默认 - 56
 */
import T from "./O.to";
import Abs from '../../abyss';
import Ele from '../../element';
import Ut from '../../unity';
import Ajx from '../../ajax';
import Dev from "../../develop";

/**
 * ## 「标准」`Ux.rxResize`
 *
 * 设置窗口 resize 的事件专用。
 *
 * @memberOf module:_engine
 * @param {Object} reference React对应组件引用。
 * @returns {Function} 返回 resize 回调函数。
 */
function rxResize(reference) {
    if (2 === arguments.length) {
        // 第二种用法
        return () => {
            const state = T.toHeightState(arguments[1]);
            try {
                reference.setState(state);
            } catch (error) {

            }
        }
    } else {
        // 目前存在的用法
        return (adjust = 0) => {
            const state = T.toHeightState(adjust);
            try {
                reference.setState(state);
            } catch (error) {

            }
        };
    }
}

/**
 * ## 「标准」`Ux.rxCheckedRow`
 *
 * 表格 Table 中的专用 selected 函数生成器。
 *
 * @memberOf module:_engine
 * @param {Object} reference React对应组件引用。
 * @param {String} field 字段名称。
 * @returns {Function} 选中函数。
 */
const rxCheckedRow = (reference, field = "$selected") => (keys = []) => {
    let state = reference.state ? reference.state : {};
    state = Abs.clone(state);
    const {$data = []} = state;
    if (0 < $data.length) {
        const selected = $data.filter(item => keys.includes(item.key));
        // const original = Dsl.getArray($selected);
        // selected.forEach(each => original.saveElement(each));
        // let checked = original.to();
        // 此处适配多选，所以旧代码不能保留
        state[field] = selected ? selected : [];
        reference.setState(state);
    }
};
/**
 *
 * ## 「标准」`Ux.rxCheckedTree`
 *
 * 树专用组件 Tree 中的专用选择函数，借助 $keySet 集合
 *
 * @memberOf module:_engine
 * @param {Object} reference React对应组件引用。
 * @param {Array} input 当前组中数组，本身为一棵树
 * @param {Function} callback 回调函数
 * @returns {Function} 选中函数。
 */
const rxCheckedTree = (reference, input = [], callback) => (keys = [], item) => {
    let current = [];
    const exclude = []
    Abs.itTree(input, item => {
        current.push(item.key);
        /*
         * 必须是 false 的情况，否则会将 undefined 的情况也统计在
         * exclude 过程中导致选择失效
         */
        if (false === item.checkable) {
            exclude.push(item.key);
        }
    });
    // 先找到当前 checkedKeys 所在的组
    let {$keySet} = reference.state;
    const keySet = new Set();
    // 读取最原始的
    if ($keySet) {
        Array.from($keySet).forEach(key => keySet.add(key))
    }

    // 减掉当前 current 部分
    current.forEach(key => keySet.delete(key));

    // 追加 keys 部分
    if (Abs.isArray(keys)) {
        // checkStrictly = false
        keys.forEach(key => keySet.add(key));
    } else {
        // checkStrictly = true
        const checkedKeys = Abs.clone(keys.checked);
        // 将选中项设置
        checkedKeys.forEach(key => keySet.add(key));
        {
            // 根据当前节点联动
            const flatted = Ele.elementFlat(input);
            const {node} = item;
            if (node) {
                const {data = {}} = node.props;
                if (item.checked) {
                    // 当前节点被选中
                    const children = Ele.elementChildren(flatted, data);
                    children.forEach(each => keySet.add(each.key));
                    // 选择当前节点的父节点
                    const branch = Ele.elementBranch(flatted, data.key);
                    branch.forEach(each => keySet.add(each.key));
                } else {
                    // 当前节点被反选
                    const children = Ele.elementChildren(flatted, data);
                    children.forEach(each => keySet.delete(each.key));
                    /*
                     * 反选需要检查父节点的兄弟节点
                     * 兼容性读取：parent / parentId
                     */
                    const parents = [];
                    {
                        // parentId 的分树
                        const branchId = Ele.elementParent(flatted, data.key);
                        // parent标准分树
                        const branch = Ele.elementParent(flatted, data.key, "parentId");
                        branchId.concat(branch).forEach(parent => parents.push(parent));
                    }
                    /*
                     * 构造函数，针对当前父类的每个类执行递归检查
                     * 逆序检查，先检查父类，然后父类被移除过后，再检查当前类
                     * 这样才会导致最终的递归效果。
                     */
                    parents.reverse().forEach(parent => {
                        /*
                         * 查找同层兄弟节点
                         */
                        const children = Ele.elementChildren(flatted, parent);
                        const selected = children.filter(item => keySet.has(item.key));

                        if (0 === selected.length) {
                            /*
                             * 无子节点选中，则直接取消父节点
                             */
                            keySet.delete(parent.key);
                        }
                    })
                }
            }
        }
    }
    // 移除不可选的
    Array.from(exclude).forEach(key => keySet.delete(key));
    const state = {};
    state.$keySet = keySet;
    if (Abs.isFunction(callback)) {
        const append = callback(keySet);
        if (append) {
            Object.assign(state, append);
        }
    }
    reference.setState(state);
}

// ----------------------------- 常用事件处理方法 ------------------------------
/*
 * 工作流部分最后会使用此处相关操作
 * 单纯打开/关闭窗口
 * - 添加按钮
 * - 编辑按钮
 * 该操作会设置核心变量，变量如下
 * -- $inited: 表单初始化方法
 * -- $visible：窗口显示或隐藏
 * -- $loading：窗口是否正在加载
 * -- $submitting：窗口处于正在提交
 */

// ----------------------------- rxShow
const rxShow = (reference, addOn = {}) => {
    const {$inited, state = {}} = addOn;
    const updated = Abs.clone(state);
    updated.$visible = true;
    updated.$submitting = false;
    updated.$loading = false;
    updated.$inited = $inited;
    reference.setState(updated);
}
const rxShowFn = (reference, addOn = {}) => (event) => {
    Abs.prevent(event);
    rxShow(reference, addOn);
}
const rxOpen = (reference, addOn = {}, rxRunner) => {
    const {
        state = {},
        $inited = {}
    } = addOn;
    Ajx.asyncWrap(reference, {}, () => rxRunner($inited)).then($queried => {
        const stateShow = {};
        stateShow.state = state;
        stateShow.$inited = $queried;
        rxShow(reference, stateShow);
    });
}
const rxOpenFn = (reference, addOn = {}, rxRunner) => (event) => {
    Abs.prevent(event);
    rxOpen(reference, addOn, rxRunner);
}
// ----------------------------- rxClose
const rxClose = (reference, addOn = {}) => {
    const {
        state = {},
        pRefresh = false,   // 是否刷新界面
    } = addOn;
    const updated = Abs.clone(state);
    updated.$visible = false;
    updated.$submitting = false;
    updated.$loading = false;
    updated.$inited = undefined;
    if (pRefresh) {
        updated.$refresh = Ut.randomString(12);
    }
    reference.setState(updated);
}
const rxCloseFn = (reference, addOn = {}) => (event) => {
    Abs.prevent(event);
    rxClose(reference, addOn);
}

// ----------------------------- rxRow
const rxExecute = (reference, addOn, rxRunner) => {
    const {
        $inited,
        state = {},
        // 响应专用信息
        ...parameters
    } = addOn;
    const {
        pRefresh = true,
        pMessageKey = "success",
        pMessage = {},
        ...rest
    } = parameters;
    Dev.dgDebug(parameters, "行执行 Executor 构造参数！", "#8B1C62");
    let promise;
    if (Abs.isFunction(rxRunner)) {
        promise = Ajx.asyncWrap(reference, {
            failure: pMessage.error,
            success: pMessage[pMessageKey],
        }, () => rxRunner($inited, rest));
    } else {
        promise = Abs.promise($inited);
    }
    return promise.then(() => {
        // 响应信息处理
        const updated = Abs.clone(state);
        if (pRefresh) {
            updated.$refresh = Ut.randomString(12);
        }
        if (!Abs.isEmpty(updated)) {
            reference.setState(updated);
        }
    })
}
const rxRow = (reference, addOn = {}, rxRunner) =>
    rxExecute(reference, addOn, rxRunner)
const rxRowFn = (reference, addOn = {}, rxRunner) => (event) => {
    Abs.prevent(event);
    rxRow(reference, addOn, rxRunner);
}
// ----------------------------- rxSelected
const rxSelected = (reference, addOn = {}, rxRunner) => {
    const {
        state = {},
    } = addOn;
    const updated = Abs.clone(state);
    Ajx.asyncWrap(reference, {}, rxRunner).then($selected => {
        updated.$selected = $selected;
        reference.setState(updated);
    });
}
const rxSelectedFn = (reference, addOn = {}) => ($selected = []) => {
    rxSelected(reference, addOn, () => Abs.promise($selected));
}

// ----------------------------- rxBatch
const rxBatch = (reference, addOn = {}, rxRunner) =>
    rxExecute(reference, addOn, rxRunner)
const rxBatchFn = (reference, addOn = {}, rxRunner) => (params = {}) => {
    const parameters = {
        ...addOn,
        ...params
    }
    Dev.dgDebug(parameters, "批量 Executor 构造参数！", "#8B1C62");
    rxBatch(reference, parameters, rxRunner)
}
// eslint-disable-next-line import/no-anonymous-default-export
export default {
    // 函数处理
    rxResize,
    rxCheckedRow,
    rxCheckedTree,
    // 快速开发核心方法（函数Action专用）
    /*
     * 统一数据结构
     * {
     *     $inited: {},     // 记录数据信息
     *     state: {},       // 附加状态信息
     *     ...rest: {},     // 构造成 config 状态信息
     * }
     */
    // 显示窗口
    rxShow,
    rxShowFn,
    // 关闭窗口
    rxClose,
    rxCloseFn,
    // 行执行操作
    rxRow,
    rxRowFn,
    // 行选择操作
    rxSelected,
    rxSelectedFn,
    // 行打开操作
    rxOpen,
    rxOpenFn,
    // 批量操作
    rxBatch,
    rxBatchFn,
}